﻿/**
* CRL
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CRL.Core
{
    /// <summary>
    /// 自定义CallContext
    /// </summary>
    public class CallContext
    {
#if NETSTANDARD
        static ConcurrentDictionary<string, AsyncLocal<object>> localDatas = new ConcurrentDictionary<string, AsyncLocal<object>>();
        public static T GetData<T>(string contextName)
        {
            var a2 = localDatas.TryGetValue("ThreadWorkCurrent", out var thread);
            if (a2 && thread.Value != null)
            {
                showWarning(thread.Value.ToString(), contextName);
            }
            var a = localDatas.TryGetValue(contextName, out AsyncLocal<object> v);
            if (a)
            {
                if (v.Value == null)
                {
                    return default(T);
                }
                return (T)v.Value;
            }
            return default(T);
        }
        public static void SetData(string contextName, object data)
        {
            var a = localDatas.TryGetValue(contextName, out AsyncLocal<object> v);
            if (a)
            {
                v.Value = data;
            }
            else
            {
                localDatas.TryAdd(contextName, new AsyncLocal<object>() { Value = data });
            }
        }
#else

        public static T GetData<T>(string contextName)
        {
            //rem 不能使用LogicalGetData,会造自定义线程只有一个实例
            var thread = System.Runtime.Remoting.Messaging.CallContext.GetData("ThreadWorkCurrent");
            if (thread != null)
            {
                showWarning(thread.ToString(), contextName);
            }
            //return default(T);
            var dbContextObj = System.Runtime.Remoting.Messaging.CallContext.GetData(contextName);
            if (dbContextObj == null)
                return default(T);
            return (T)dbContextObj;
        }
        public static void SetData(string contextName, object data)
        {
            //return;
            System.Runtime.Remoting.Messaging.CallContext.SetData(contextName, data);
        }
#endif
        static ConcurrentDictionary<string, string> logDic = new ConcurrentDictionary<string, string>();
        static void showWarning(string threadName, string contextName)
        {
            if (logDic.TryGetValue(contextName, out var v))
            {
                return;
            }
            var msg = $"线程[{threadName}]内使用了CallContext，可能会导致数据混乱 context:{contextName}";
            Console.WriteLine(msg);
            EventLog.Log(msg, "CallContext");
            logDic.TryAdd(contextName,"");
        }
        public static T GetData<T>(string contextName, Func<T> callBack)
        {
            var obj = GetData<ContextObj<T>>(contextName);
            if (obj == null)
            {
                obj = new ContextObj<T> { Data = callBack() };
                SetData(contextName, obj);
            }
            return obj.Data;
        }

        class ContextObj<T>
        {
            public T Data;
        }
    }
}
